//为什么用TS?
// 1.ts是js的加强版，给js增加了可选的静态类型和面向对象编程。
// 2.ts是面向对象语言，包含类和接口的概念。
// 3.ts开发时就能给出编译错误，js运行时才能发现。
// 4.ts作为强类型语言，明确数据类型，代码可读性比较强。
// 5.ts可选链属性。


//基础类型
let str:string = "大林忽悠"
let num:number = NaN
let bool:boolean = true
let sym:symbol = Symbol()
let n:null = null
let u:undefined = undefined

console.log(str)

//any 任意类型  unknown 不知道的类型
//类型等级：
//1.any unknown  top type 顶级类型 
//2.Object
//3.Number String Boolean...  自身构造函数
//4.number string boolean...  实例对象
//5.1  '大林忽悠'  false...
//6.never
//unknown只能赋值给自身或者any
//unknown没有办法读任何属性，方法也不可以调用
//unknown比any安全

//Object 和 object
//Object可以任意赋值
//object常用于类型约束，除了原始类型外的引用类型

let a:{} = {num: 1}  //new Object   无法对变量进行任何赋值操作

//interface 重名的会进行重合
//interface 任意key  [propName:strng]:any  (索引签名)
//interface ?操作符(可选值)  readonly(让值不可更改)
//interface 接口继承  extends
//interface 定义函数类型
//不能多属性，也不能少属性
interface Axxsxs{
    name:string
    age?:number
    readonly cb:()=>boolean
}
let x:Axxsxs = {
    name: '大林忽悠',
    age: 25,
    cb:()=>{
        return false
    }
}

interface Fn {
    (name:string):number[]
}
const fn:Fn = function (name:string) {
    return [1]
}

//数组普通类型
//let arr:number[] = [1,2,3]
//let arr:boolean[] = [true,false]
//let arr:Array<boolean> = [true,false]
//对象数组
interface X {
    name:string
    age?:number
}

let arr:X[] = [{name: "大林忽悠"}]

//二维数组
let array:number[][] = [[1],[2],[3]]
//let array:Array<Array<number>> = [[1],[2],[3]]
//杂烩数组
//let array:any[] = [1,'asd',true,{}]


function fn2(...args:any[]) {
    let a:IArguments = arguments  //定义类数组
   console.log(args)
}

fn2(1,2,3)

//1.函数定义类型和返回值 | 箭头函数定义类型和返回值
//2.函数默认参数 | 函数可选参数
//3.参数是一个对象如何定义
//4.函数this类型
//5.函数重载

// function add(a:number = 10,b?:number):number {  默认值和可选
//     return a + b 
// }
const add = (a:number,b:number):number => a+b

interface User {
    name:string
    age?:number
}

function P(user:User):User {
    return user
}

console.log(P({name: '大林忽悠',age: 25}))

interface Obj {
    user:number[]
    add:(this:Obj,num:number)=>void
}
//ts可以定义this类型  必须是第一个参数定义this的类型
let obj:Obj = {
    user:[1,2,3],
    add(this:Obj,num:number) {
        this.user.push(num)
    }
}

obj.add(4)
console.log(obj)


//函数重载，根据参数不同重载不同函数返回不同结果
let user:number[] = [1,2,3]
function findNum(add:number[]):number[]  
function findNum(id:number[]):number[]
function findNum():number[]
function findNum(ids?:number[] | number[]):number[] {
    if(typeof ids == 'number') {
        return user.filter(v=>v == ids)
    }else if (Array.isArray(ids)){
        user.push(...ids)
        return user
    }else {
        return user
    }
}

//let phone:number | string   联合类型
//交叉类型
interface People{
    name:string
    age:number
}

interface Man{
    sex:number
}

const dalin = (man:People & Man):void => {
    console.log(man)
}
dalin({
    name: '大林忽悠',
    age: 25,
    sex: 1
})
//类型断言   a as string  <string>a

//内置对象
//let num:Number = new Number(1)
let date:Date = new Date()
let reg:RegExp = new RegExp(/\w/)

//let div = document.querySelector('footer') as Element
//let div:NodeList = document.querySelectorAll('footer')
let div:NodeListOf<HTMLDivElement | HTMLElement> = document.querySelectorAll('div')

let local:Storage = localStorage
let lo:Location = location
let promise:Promise<number> = new Promise((r)=>r(1))
let cookie:string = document.cookie

//1.class的基本用法  继承(extends)  和  类型约束(implements)
//2.class的修饰符 readonly(不可修改,只读) private(私有属性) protected(可继承私有) public
//3.super原理
//4.静态方法(static),只能通过类去调用
//5.get set

interface Options {
    el:string | HTMLElement
}

interface VueCls {
    options:Options
    init():void
}

interface Vnode {
    tag:string
    text?:string
    children?:Vnode[]
}

//虚拟dom 简单版
class Dom {
    //创建节点的方法
    private createElement(el:string) {
        return document.createElement(el)
    }
    //填充文本的方法
    private setText (el:HTMLElement, text:string | null) {
        el.textContent = text
    }
    //渲染函数
    protected render(data:Vnode) {
        let root = this.createElement(data.tag)
        if(data.children && Array.isArray(data.children)) {
            data.children.forEach(item => {
                let child = this.render(item)
                root.appendChild(child)
            })
        } else {
            data.text && this.setText(root, data.text)
        }
        return root
    }
}

class Vue extends Dom implements VueCls{
    options: Options
    constructor (options: Options) {
        super()    //父类的prototype.constructor.call
        this.options = options
        this.init()
    }
    public init(): void {
        let data:Vnode = {
            tag: "div",
            children: [
                {
                   tag: "section",
                   text: "我是子节点1"
                },
                {
                    tag: "section",
                    text: "我是子节点2"
                 },
            ]
        }
        let app = typeof this.options.el == 'string' ? document.querySelector(this.options.el) : this.options.el
        app && app.appendChild(this.render(data))
    }
}

new Vue({
    el: "#app"
})


//基类 抽象类
//abstract所定义的类
//abstract所定义的方法都只能描述不能进行实现
//抽象类无法实例化但可以被派生类继承
abstract class King {
    name:string
    constructor (name?:string) {
        this.name = name
    }
    getName ():string{
        return this.name
    }
    abstract init(name:string):void
}

class Lin extends King{
    constructor () {
        super()
    }
    init (name:string) {

    }
    setName (name:string) {
        this.name = name
    }
}

const lin = new Lin()
lin.setName('大林忽悠')

console.log(lin.getName())

//元组：规定了数量，对应位置对应类型
let arr2:[number,boolean] = [1,false]

//枚举类型 （数字枚举 字符串枚举 异构枚举 接口枚举 const枚举 反向映射）
enum Color {  //red = 0, green = 1, blue = 2
    red = 1,  //red = 1, green = 2, blue = 3  增长枚举
    green,
    blue
}

enum Types {
    success
}

let success:number = Types.success
let key = Types[success]

console.log(`value---${success}`,`key---${key}`)

//类型别名
//与interface的区别：
//1.不能用extends去继承（只能用交叉类型 & ）
//2.可以用联合类型 | ，interface不可以
//3.interface重名合并，type不会
type s = number[] | string
let str1:s = '大林忽悠'

//extends 在 types 是包含的意思（左边的值会作为右边类型的子类型）
type num = 1 extends number ? 1 : 0

//Symbol
//for会全局寻找有没有注册过这个key，有直接用，没有就创建
//Symbol.for('lin') === Symbol.for('lin')  true

//迭代器  [Symbol.iterator]
//迭代器语法糖  for of
//解构  数组解构底层原理是迭代器  对象不是

//泛型 <T,K>
//泛型约束  <T extends number>
//keyof  把对象的key推断成联合类型  如下面obj1变成 name | sex
function lin1<T>(a:T,b:T):Array<T>{
    return [a,b]
}

let obj1 = {
    name: '大林忽悠',
    sex: '男'
}

type Key = keyof typeof obj1

function ob<T extends object,K extends keyof T>(obj:T,key:K){
    return obj[key]
}

ob(obj1, 'name')

//高级用法 Partial
interface User {
  id: number;
  name: string;
  age0: number;
}

// 使用 Partial 后，所有属性变为可选
type PartialUser = Partial<User>;

// 等效于：
// type PartialUser = {
//   id?: number;
//   name?: string;
//   age?: number;
// };

//for (let key in obj)
type Options1<T extends object> = {
    readonly [key in keyof T]:T[key]
}

type B = Options1<Data>


//配置文件tsconfig.json
//namespace
//声明文件 d.ts
//Mixins混入


//1.类装饰器 ClassDecorator  target 构造函数
//2.属性装饰器 PropertyDecorator
//3.参数装饰器 ParameterDecorator
//4.方法装饰器 MethodDecorator PropertyDescriptor
//5.装饰器工厂
//6.import 'reflect-metadata'
//7.axios

const Base = (name:string) => {
    const fn:ClassDecorator = (target) => {
        console.log(target)
        target.prototype.lin = name  //给Http构造函数增加属性
        target.prototype.fn = () => {     //给Http构造函数增加方法
            console.log('hello')
        }
    }
    return fn
}

const Get = (url:string) => {
    const fn:MethodDecorator = (target,_key,descriptor:PropertyDescriptor) => {
        // const key = Reflect.getMetadata('key',target)
        // axios.get(url).then(res=>{
        //     descriptor.value(key ? res.data[key] : res.data)
        // })
    }
    return fn
}

const Result = () => {
    const fn:ParameterDecorator = (target,key,index) => {
        // Reflect.defineMetadata('key','result',target)
    }
    return fn
}

const Name:PropertyDecorator = (target,key) => {

}

@Base('大林忽悠')  //装饰器工厂  闭包  函数柯里化
class Http {
    //...
    @Name
    lin:string
    constructor () {
        this.lin = '大林忽悠'
    }
    @Get('https://api...')
    getList (@Result() data:any) {
        // console.log(data.result.list)
        console.log(data)
    }
}
const http = new Http() as any

console.log(http.lin)

//Paritial 把某个类型里的属性全部变成可选项
//Required 把某个类型里的属性全部变成必选项
//Readonly 把属性变成只读
//Record<T,K> 将K中所有属性的值转化为T类型
interface PageInfo{
    title:string
}
type page = 'a'|'b'|'c'
type mix = Record<PageInfo, page>
const xx:mix = {
    'a':{title:'xxx'},
    'b':{title:'xxx'},
    'c':{title:'xxx'}
}
//Exclude<T,U> 将其中某个类型中属于另一个类型的去掉。
type aa = Exclude<'a'|'b'|'c','a'> //只剩下'b'|'c'
type bb = Exclude<'a'|'b'|'c','a'|'b'> //只剩下'c'
//Extract<T,U>将t,u中的交集提取出来

//问题：如何基于一个已有类型，扩展出大部分内容相似，但有部分区别的类型？
//通过Pick和Omit
interface test {
    name:string,
    sex:string,
    height:number
}

type Sex = Pick<test, 'sex'>
const aaa:Sex = {sex:'女'}
type WithoutSex = Omit<test, 'sex'>
const b:WithoutSex = {name: '11',height: 160}